<?php

namespace app\admin\controller\shop\order;

use app\admin\controller\shop\Base;
use app\admin\model\shop\delivery\Address;
use app\admin\model\shop\delivery\Company;
use app\shop\enums\order\refund\RefundAuditStatus;
use app\shop\enums\order\refund\RefundReceiveStatus;
use app\shop\enums\order\refund\RefundStatus;
use app\shop\enums\order\refund\RefundType;
use app\shop\library\calculate\Calculate;
use app\shop\library\pay\PayProvider;
use app\shop\library\ShopHelper;
use app\shop\service\LogService;
use think\db\exception\DbException;
use Throwable;

/**
 * 售后单记录管理
 */
class Refund extends Base
{
    /**
     * Refund模型对象
     * @var object
     * @phpstan-var \app\admin\model\shop\order\Refund
     */
    protected object $model;

    protected array|string $preExcludeFields = [];

    protected array $withJoinTable = [
        'user'          => [
            'nickname', 'openid'
        ],
        'orderRelation' => [
            'order_no', 'type', 'pay_status', 'pay_price', 'freight'
        ]
    ];

    protected string|array $quickSearchField = ['orderRelation.order_no'];

    protected array $noNeedPermission = [];

    protected string $validateName = \app\admin\validate\shop\order\Refund::class;

    public function initialize(): void
    {
        parent::initialize();
        $this->model = new \app\admin\model\shop\order\Refund;
    }


    /**
     * 若需重写查看、编辑、删除等方法，请复制 @see \app\admin\library\traits\Backend 中对应的方法至此进行重写
     */

    /**
     * 列表
     * @return void
     * @throws Throwable
     * @throws DbException
     */
    public function index(): void
    {
        if ($this->request->param('select')) {
            $this->select();
        }

        list($where, $alias, $limit, $order) = $this->queryBuilder();
        $res = $this->model
            ->field($this->indexField)
            ->withJoin($this->withJoinTable, $this->withJoinType)
            ->alias($alias)
            ->where($where)
            ->with(['detail'])
            ->order($order)
            ->paginate($limit);

        $this->success('', [
            'list'       => $res->items(),
            'total'      => $res->total(),
            'remark'     => get_route_remark(),
            'statusInfo' => $this->statusInfo(),
        ]);
    }

    public function statusInfo(): array
    {
        $statusData = RefundStatus::getData();

        $auditStatusData   = RefundAuditStatus::getData();
        $tipAuditStatusArr = [RefundAuditStatus::WAIT_AUDIT];

        $auditStatusCount = $this->model
            ->whereIn('audit_status', $tipAuditStatusArr)
            ->field(['count(*) as count', 'audit_status'])
            ->group('audit_status')
            ->column('count(*) as count', 'audit_status');

        foreach ($auditStatusData as &$value) {
            $value['count'] = $auditStatusCount[$value['value']] ?? 0;
        }

        return [
            'status'       => $statusData,
            'audit_status' => $auditStatusData,
        ];
    }

    /**
     * 审核
     */
    public function audit(): void
    {
        $params = $this->request->param();

        $this->validateData($params, 'audit');
        [
            'id'                => $id,
            'audit_status'      => $auditStatus,
            'refund_address_id' => $addressId,
            'refuse_reason'     => $refuseReason,
        ] = $params;

        $refund = $this->model->where('id', $id)->findOrEmpty();
        if ($refund->isEmpty()) {
            $this->error('售后单异常,请刷新');
        }
        $this->model->startTrans();
        try {
            if ($auditStatus == RefundAuditStatus::AUDIT_REJECT) {
                //拒绝
                $refund->status        = RefundStatus::COMPLETED;
                $refund->refuse_reason = $refuseReason;

                LogService::orderStatusLog($refund->order_id, 0, "拒绝售后申请", $this->auth->id, true);
            } else {
                $refundAddress = Address::where('id', $addressId)->findOrEmpty();
                if ($refundAddress->isEmpty()) {
                    $this->error('退货地址不存在');
                }
                $refundAddress = $refundAddress->toArray();

                $refund->refund_address = [
                    'name'         => $refundAddress['name'],
                    'phone'        => $refundAddress['phone'],
                    'full_address' => $refundAddress['full_address']
                ];
            }

            $refund->audit_status = $auditStatus;
            $refund->save();

            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            $this->error('审核失败', $e->getMessage());
        }

        $this->success('审核成功');
    }

    /**
     * 确认收货
     */
    public function receive(): void
    {
        $params = $this->request->param();

        $refund = $this->model->where('id', $params['id'])->findOrEmpty();
        if ($refund->isEmpty()) {
            $this->error('售后订单不存在');
        }

        if ($refund->refund_type === RefundType::RETURN_REFUND) {
            $this->validateData($params, 'receive');
            $this->refundHandle($refund, $params);
        }

        if ($refund->refund_type === RefundType::EXCHANGE) {
            $this->validateData($params, 'exchange');
            $this->exchangeHandle($refund, $params);
        }
    }

    /**
     * 退货退款处理
     * @param       $refund
     * @param array $params
     * @return void
     */
    private function refundHandle($refund, array $params): void
    {
        [
            'refund_money'   => $refundMoney,
            'refund_freight' => $refundFreight,
        ] = $params;

        $orderModel = new \app\admin\model\shop\order\Order();
        $order      = $orderModel->where('id', $refund->order_id)->with(['detail'])->findOrEmpty();
        if (!$order->detail) {
            $this->error('售后订单信息异常');
        }
        if ($refundFreight > $order->freight) {
            $this->error('退款运费超出订单总运费');
        }
        foreach ($order->detail as $detail) {
            if ($detail->id === $refund->order_detail_id && $refundMoney > $detail->pay_price) {
                $this->error('退款金额超出订单支付金额');
            }
        }

        $this->model->startTrans();
        try {
            //退款
            $refundAmount = Calculate::newDecimal($refundMoney)->add($refundFreight)->getValue();
            (new PayProvider($order->pay_way))->refund([
                'out_trade_no'  => $order->out_trade_no,
                'source'        => $order->source,
                'refund_amount' => $refundAmount,
                'total_amount'  => $order->pay_price,
            ]);

            $order->inc('refund_amount', $refundAmount)->inc('refund_num', $refund->refund_num)->save();

            // 更新订单售后状态
            $this->model->where('id', $params['id'])->update([
                'status'         => RefundStatus::COMPLETED,
                'receive_status' => RefundReceiveStatus::RECEIVE,
                'receive_time'   => time(),
                'refund_money'   => $refundMoney,
                'refund_freight' => $refundFreight,
            ]);

            LogService::orderStatusLog($refund->order_id, 0, '已完成用户售后申请(退货退款)', $this->auth->id, true);

            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            $this->error('确认收货失败', $e->getMessage());
        }

        $this->success('确认收货成功');
    }

    /**
     * 换货处理
     * @param       $refund
     * @param array $params
     * @return void
     */
    private function exchangeHandle($refund, array $params): void
    {
        [
            'delivery_company_id' => $deliveryCompanyId,
            'delivery_no'         => $deliveryNo,
        ] = $params;

        $deliveryCompany = Company::where('id', $deliveryCompanyId)->findOrEmpty();
        if ($deliveryCompany->isEmpty()) {
            $this->error('物流信息有误');
        }

        $this->model->startTrans();
        try {
            $this->model->where('id', $refund->id)->update([
                'status'            => RefundStatus::COMPLETED,
                'receive_status'    => RefundReceiveStatus::RECEIVE,
                'receive_time'      => time(),
                'exchange_delivery' => ShopHelper::array_to_json([
                    'delivery_company'      => $deliveryCompany->name,
                    'delivery_company_code' => $deliveryCompany->code,
                    'delivery_no'           => $deliveryNo
                ])
            ]);

            LogService::orderStatusLog($refund->order_id, 0, '已完成用户售后申请(换货)', $this->auth->id, true);

            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            $this->error('确认收货失败', $e->getMessage());
        }

        $this->success('确认收货成功');
    }
}